import cairo

# psyco makes this almost 1/3 faster: for 200x200 black stone,
# 2.8 sec -> 2.0 sec.
import psyco; psyco.full()

import math

class _WhiteDesc(object):
    __slots__ = ['cosTheta', 'sinTheta', 'stripeWidth', 'xAdd', 'stripeMul', 'zMul']

def drawStone_newPics():
    ###
    ### BLACK STONE
    ###

    size = 100
    DRAWSTONE_NUMWHITE = 10 # cgoban: 5

    import array
    width = height = size
    black_data = array.array('c', 'a'*width*height*4)
    white_data = [array.array('c', 'a'*width*height*4) for i in range(DRAWSTONE_NUMWHITE)]

    white = [decideAppearance(i) for i in range(DRAWSTONE_NUMWHITE)]

    for x in range(size):
        for y in range(size):

            # mask -- binary only so far, but TODO: should be antialiased!
            if (x-size/2)**2+(y-size/2)**2 > (size/2)**2:
                continue

            bright,lambertian,z = calcCosAngleReflection2View((size - 1) - (x+x),
                                                              (size - 1) - (y + y),
                                                              size+size)
            bright *= bright;
            bright *= bright;
            bBright = bright*165.0 + lambertian*10.0 - 5.0;
            bright *= bright;
            bright *= bright;
            bright *= bright; # (used for white stones, below)
            bBright = int(bBright) # (new!)
            if (bBright > 255):
                bBright = 255;
            if (bBright < 0):
                bBright = 0;
            black_data[(x + y*width)*4 + 0] = chr(bBright)
            black_data[(x + y*width)*4 + 1] = chr(bBright)
            black_data[(x + y*width)*4 + 2] = chr(bBright)
            black_data[(x + y*width)*4 + 3] = chr(255)

            # fyi: for black stones, bBright ranges from 0 to 169.x

            ##
            ## WHITE STONES
            ##

            for i in range(DRAWSTONE_NUMWHITE):
                wStripeLoc = (x*white[i].cosTheta - y*white[i].sinTheta) + white[i].xAdd;
                wStripeColor = ((wStripeLoc + (z * z * z * white[i].zMul) * white[i].stripeWidth) % white[i].stripeWidth) / white[i].stripeWidth
                wStripeColor = wStripeColor * white[i].stripeMul - 0.5;
                if (wStripeColor < 0.0):
                    wStripeColor = -2.0 * wStripeColor;
                if (wStripeColor > 1.0):
                    wStripeColor = 1.0;
                wStripeColor = wStripeColor * 0.15 + 0.85;
                wBright = bright*bright*250.0 + \
                          wStripeColor * (lambertian*120.0 + 110.0);
                if (wBright > 255):
                    wBright = 255;
                if (wBright < 0):
                    wBright = 0;
                wBright = int(wBright) # (new!)

                baseColor = 50 # ??? -- what does cgoban use?
                value = min(baseColor + wBright, 255)
                white_data[i][(x + y*width)*4 + 0] = chr(value)
                white_data[i][(x + y*width)*4 + 1] = chr(value)
                white_data[i][(x + y*width)*4 + 2] = chr(value)
                white_data[i][(x + y*width)*4 + 3] = chr(255)

    
    stride = width * 4
    black_stone_surface = cairo.ImageSurface.create_for_data(black_data,
                                                             cairo.FORMAT_ARGB32,
                                                             width, height, stride)
    black_stone_surface.write_to_png("gimme-black-stone.png")
    for i in range(DRAWSTONE_NUMWHITE):
        white_stone_surface = cairo.ImageSurface.create_for_data(white_data[i],
                                                                 cairo.FORMAT_ARGB32,
                                                                 width, height, stride)
        white_stone_surface.write_to_png("gimme-white-stone-%d.png" % i)

def calcCosAngleReflection2View(x, y, r):
    lx = 0.35355339
    ly = 0.35355339
    lz = 0.8660254

    nz = math.sqrt(float(r * r - x * x - y * y))
    z = 1.0 - (nz / r);
    nx = float(x)
    ny = float(y)
    nDotL = (nx*lx + ny*ly + nz*lz) / r;
    rz = (2.0 * nz * nDotL) / r - lz;
    lambertian = nDotL
    return rz, lambertian, z

import random

def decideAppearance(size):

    minStripeW = float(size) / 20.0;
    if (minStripeW < 2.5):
        minStripeW = 2.5;
    maxStripeW = float(size) / 5.0;
    if (maxStripeW < 4.0):
        maxStripeW = 4.0;
    theta = random.random() * 2.0 * math.pi

    desc = _WhiteDesc()
    desc.cosTheta = math.cos(theta);
    desc.sinTheta = math.sin(theta);
    desc.stripeWidth = minStripeW + \
                        (random.random() * (maxStripeW - minStripeW));
    desc.xAdd = random.random() * desc.stripeWidth + \
                 float(size) * 3;  # Make sure that all x's are positive!
    desc.stripeMul = random.random() * 4.0 + 1.5;
    desc.zMul = random.random() * 650.0 + 70.0;
    return desc

drawStone_newPics() # TRY IT! ##############################

"""
FUTURE:
- optimize:
    - a*=a; a*=a; ... can be just a**(a**n), right?
    - calcCosAngleReflection2View does float() calls when not really necessary
- antialiasing of circle mask
"""
